<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>β2受体激动剂作用机制可视化教学平台</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            min-height: 100vh;
            padding: 20px;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: white;
            border-radius: 20px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.1);
            overflow: hidden;
        }

        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 30px;
            text-align: center;
        }

        .header h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
            font-weight: 700;
        }

        .header p {
            font-size: 1.2rem;
            opacity: 0.9;
        }

        .main-content {
            padding: 40px;
        }

        .controls {
            text-align: center;
            margin-bottom: 30px;
        }

        .btn-primary {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            padding: 15px 40px;
            font-size: 1.1rem;
            border-radius: 50px;
            cursor: pointer;
            transition: all 0.3s ease;
            box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
        }

        .btn-primary:hover {
            transform: translateY(-2px);
            box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
        }

        .btn-primary:active {
            transform: translateY(0);
        }

        .btn-primary:disabled {
            opacity: 0.6;
            cursor: not-allowed;
            transform: none;
        }

        #flowchart {
            width: 100%;
            height: 600px;
            border: 1px solid #e0e0e0;
            border-radius: 10px;
            background: #fafafa;
            position: relative;
            overflow: hidden;
        }

        .node {
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .node:hover {
            filter: brightness(1.1);
        }

        .node.active {
            filter: brightness(1.2);
            stroke-width: 3;
        }

        .link {
            fill: none;
            stroke: #999;
            stroke-width: 2;
            transition: all 0.3s ease;
        }

        .link.active {
            stroke: #667eea;
            stroke-width: 4;
            filter: drop-shadow(0 0 5px rgba(102, 126, 234, 0.5));
        }

        .node-label {
            font-size: 14px;
            font-weight: 600;
            text-anchor: middle;
            pointer-events: none;
            fill: #333;
        }

        .tooltip {
            position: absolute;
            background: rgba(0, 0, 0, 0.9);
            color: white;
            padding: 15px;
            border-radius: 8px;
            font-size: 14px;
            max-width: 300px;
            z-index: 1000;
            pointer-events: none;
            opacity: 0;
            transition: opacity 0.3s ease;
            box-shadow: 0 4px 15px rgba(0,0,0,0.3);
        }

        .tooltip.show {
            opacity: 1;
        }

        .tooltip h3 {
            margin-bottom: 8px;
            color: #667eea;
            font-size: 16px;
        }

        .tooltip p {
            line-height: 1.5;
            margin-bottom: 5px;
        }

        .legend {
            margin-top: 20px;
            padding: 20px;
            background: #f8f9fa;
            border-radius: 10px;
            border-left: 4px solid #667eea;
        }

        .legend h3 {
            margin-bottom: 10px;
            color: #333;
        }

        .legend-item {
            display: inline-block;
            margin-right: 20px;
            margin-bottom: 5px;
            font-size: 14px;
        }

        .legend-color {
            display: inline-block;
            width: 20px;
            height: 20px;
            border-radius: 3px;
            margin-right: 8px;
            vertical-align: middle;
        }

        .status-indicator {
            position: fixed;
            top: 20px;
            right: 20px;
            background: rgba(102, 126, 234, 0.9);
            color: white;
            padding: 10px 20px;
            border-radius: 25px;
            font-size: 14px;
            opacity: 0;
            transition: opacity 0.3s ease;
            z-index: 1001;
        }

        .status-indicator.show {
            opacity: 1;
        }

        @media (max-width: 768px) {
            .header h1 {
                font-size: 2rem;
            }
            
            .header p {
                font-size: 1rem;
            }
            
            .main-content {
                padding: 20px;
            }
            
            #flowchart {
                height: 500px;
            }
            
            .btn-primary {
                padding: 12px 30px;
                font-size: 1rem;
            }
        }

        @keyframes pulse {
            0% {
                transform: scale(1);
                opacity: 1;
            }
            50% {
                transform: scale(1.05);
                opacity: 0.8;
            }
            100% {
                transform: scale(1);
                opacity: 1;
            }
        }

        .node.pulse {
            animation: pulse 1s ease-in-out;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1><i class="fas fa-heartbeat"></i> β2受体激动剂作用机制</h1>
            <p>可视化教学平台 - 交互式药理作用流程图</p>
        </div>
        
        <div class="main-content">
            <div class="controls">
                <button class="btn-primary" id="simulateBtn">
                    <i class="fas fa-play"></i> 模拟用药过程
                </button>
            </div>
            
            <div id="flowchart"></div>
            
            <div class="legend">
                <h3><i class="fas fa-info-circle"></i> 图例说明</h3>
                <div class="legend-item">
                    <span class="legend-color" style="background: #667eea;"></span>
                    药物作用
                </div>
                <div class="legend-item">
                    <span class="legend-color" style="background: #48bb78;"></span>
                    生理效应
                </div>
                <div class="legend-item">
                    <span class="legend-color" style="background: #ed8936;"></span>
                    炎症反应
                </div>
                <div class="legend-item">
                    <span class="legend-color" style="background: #e53e3e;"></span>
                    抑制效果
                </div>
            </div>
        </div>
    </div>

    <div class="tooltip" id="tooltip"></div>
    <div class="status-indicator" id="statusIndicator"></div>

    <script>
        // 流程图数据
        const nodes = [
            { id: 'drug', label: 'β2受体激动剂', x: 100, y: 100, color: '#667eea', 
              description: 'β2受体激动剂是一类能够选择性激活β2肾上腺素受体的药物，如沙丁胺醇、特布他林等。主要用于治疗支气管哮喘和慢性阻塞性肺疾病。' },
            { id: 'receptor', label: 'β2肾上腺素受体', x: 300, y: 100, color: '#667eea',
              description: 'β2肾上腺素受体是G蛋白偶联受体家族成员，主要分布在支气管平滑肌细胞膜上。激活后可引起平滑肌松弛。' },
            { id: 'gs', label: 'Gs蛋白激活', x: 500, y: 100, color: '#667eea',
              description: 'Gs蛋白是G蛋白的一种，激活后能够刺激腺苷酸环化酶，增加细胞内cAMP的生成。' },
            { id: 'camp', label: 'cAMP水平升高', x: 700, y: 100, color: '#48bb78',
              description: 'cAMP（环磷酸腺苷）是细胞内重要的第二信使，水平升高可激活蛋白激酶A，导致平滑肌松弛。' },
            { id: 'relax', label: '支气管平滑肌松弛', x: 500, y: 250, color: '#48bb78',
              description: '通过降低细胞内钙离子浓度，使支气管平滑肌松弛，从而扩张支气管，缓解呼吸困难。' },
            { id: 'inhibit', label: '抑制炎症细胞活性', x: 700, y: 250, color: '#e53e3e',
              description: '抑制肥大细胞、嗜酸性粒细胞等炎症细胞释放炎症介质，减少气道炎症反应。' },
            { id: 'cytokine', label: '细胞因子\n(IL-4, IL-5, IL-13)', x: 200, y: 400, color: '#ed8936',
              description: '这些细胞因子在哮喘发病中起重要作用，促进炎症反应和支气管收缩。' },
            { id: 'inflammation', label: '炎症反应与支气管收缩', x: 500, y: 400, color: '#ed8936',
              description: '炎症反应导致气道水肿、黏液分泌增加和支气管收缩，引起呼吸困难。' }
        ];

        const links = [
            { source: 'drug', target: 'receptor', label: '激活' },
            { source: 'receptor', target: 'gs', label: '信号转导' },
            { source: 'gs', target: 'camp', label: '刺激' },
            { source: 'camp', target: 'relax', label: '导致' },
            { source: 'camp', target: 'inhibit', label: '抑制' },
            { source: 'cytokine', target: 'inflammation', label: '促进' },
            { source: 'relax', target: 'inflammation', label: '抵消', style: 'dashed' },
            { source: 'inhibit', target: 'inflammation', label: '间接对抗', style: 'dashed' }
        ];

        // 创建SVG
        const svg = d3.select('#flowchart')
            .append('svg')
            .attr('width', '100%')
            .attr('height', '100%')
            .attr('viewBox', '0 0 900 500');

        // 创建箭头标记
        svg.append('defs').append('marker')
            .attr('id', 'arrowhead')
            .attr('viewBox', '-0 -5 10 10')
            .attr('refX', 15)
            .attr('refY', 0)
            .attr('orient', 'auto')
            .attr('markerWidth', 8)
            .attr('markerHeight', 8)
            .append('path')
            .attr('d', 'M 0,-5 L 10,0 L 0,5')
            .attr('fill', '#999');

        // 创建连接线
        const linkElements = svg.append('g')
            .selectAll('path')
            .data(links)
            .enter()
            .append('path')
            .attr('class', 'link')
            .attr('id', d => `link-${d.source}-${d.target}`)
            .attr('d', d => {
                const source = nodes.find(n => n.id === d.source);
                const target = nodes.find(n => n.id === d.target);
                
                if (d.style === 'dashed') {
                    return `M ${source.x + 60} ${source.y + 30} Q ${(source.x + target.x) / 2} ${(source.y + target.y) / 2 - 30} ${target.x} ${target.y + 15}`;
                }
                
                if (source.y === target.y) {
                    return `M ${source.x + 60} ${source.y + 15} L ${target.x} ${target.y + 15}`;
                } else {
                    return `M ${source.x + 30} ${source.y + 30} L ${target.x + 30} ${target.y}`;
                }
            })
            .style('stroke-dasharray', d => d.style === 'dashed' ? '5,5' : 'none')
            .attr('marker-end', 'url(#arrowhead)');

        // 添加连接标签
        svg.append('g')
            .selectAll('text')
            .data(links)
            .enter()
            .append('text')
            .attr('class', 'link-label')
            .attr('x', d => {
                const source = nodes.find(n => n.id === d.source);
                const target = nodes.find(n => n.id === d.target);
                return (source.x + target.x) / 2;
            })
            .attr('y', d => {
                const source = nodes.find(n => n.id === d.source);
                const target = nodes.find(n => n.id === d.target);
                return (source.y + target.y) / 2 - 5;
            })
            .attr('text-anchor', 'middle')
            .attr('font-size', '12px')
            .attr('fill', '#666')
            .text(d => d.label);

        // 创建节点
        const nodeElements = svg.append('g')
            .selectAll('g')
            .data(nodes)
            .enter()
            .append('g')
            .attr('class', 'node-group')
            .attr('transform', d => `translate(${d.x}, ${d.y})`);

        // 添加节点矩形
        nodeElements.append('rect')
            .attr('class', 'node')
            .attr('width', 120)
            .attr('height', 50)
            .attr('rx', 8)
            .attr('ry', 8)
            .attr('fill', d => d.color)
            .attr('stroke', '#fff')
            .attr('stroke-width', 2)
            .on('click', function(event, d) {
                showTooltip(event, d);
            })
            .on('mouseenter', function(event, d) {
                d3.select(this).classed('active', true);
            })
            .on('mouseleave', function(event, d) {
                d3.select(this).classed('active', false);
            });

        // 添加节点文本
        nodeElements.append('text')
            .attr('class', 'node-label')
            .attr('x', 60)
            .attr('y', 25)
            .attr('dy', '.35em')
            .text(d => d.label)
            .style('fill', 'white')
            .style('font-weight', 'bold');

        // 工具提示功能
        const tooltip = document.getElementById('tooltip');

        function showTooltip(event, d) {
            tooltip.innerHTML = `
                <h3>${d.label}</h3>
                <p>${d.description}</p>
            `;
            tooltip.style.left = event.pageX + 10 + 'px';
            tooltip.style.top = event.pageY - 10 + 'px';
            tooltip.classList.add('show');
            
            setTimeout(() => {
                tooltip.classList.remove('show');
            }, 5000);
        }

        // 模拟用药过程
        let isSimulating = false;
        const simulateBtn = document.getElementById('simulateBtn');
        const statusIndicator = document.getElementById('statusIndicator');

        simulateBtn.addEventListener('click', simulateDrugAction);

        function simulateDrugAction() {
            if (isSimulating) return;
            
            isSimulating = true;
            simulateBtn.disabled = true;
            simulateBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 模拟中...';
            
            const steps = [
                { node: 'drug', message: 'β2受体激动剂开始发挥作用', delay: 0 },
                { node: 'receptor', message: '激活β2肾上腺素受体', delay: 1000 },
                { node: 'gs', message: 'Gs蛋白被激活', delay: 2000 },
                { node: 'camp', message: 'cAMP水平显著升高', delay: 3000 },
                { node: 'relax', message: '支气管平滑肌开始松弛', delay: 4000 },
                { node: 'inhibit', message: '炎症细胞活性被抑制', delay: 5000 }
            ];

            steps.forEach((step, index) => {
                setTimeout(() => {
                    // 高亮节点
                    d3.selectAll('.node').classed('pulse', false);
                    d3.selectAll('.link').classed('active', false);
                    
                    const node = d3.select(`.node-group:nth-child(${index + 1}) rect`);
                    node.classed('pulse', true);
                    
                    // 高亮相关连接线
                    if (index > 0) {
                        const linkId = `link-${steps[index-1].node}-${step.node}`;
                        d3.select(`#${linkId}`).classed('active', true);
                    }
                    
                    // 显示状态
                    showStatus(step.message);
                    
                    if (index === steps.length - 1) {
                        setTimeout(() => {
                            isSimulating = false;
                            simulateBtn.disabled = false;
                            simulateBtn.innerHTML = '<i class="fas fa-play"></i> 重新模拟';
                            showStatus('模拟完成！支气管扩张，炎症得到控制');
                            
                            // 重置所有高亮
                            setTimeout(() => {
                                d3.selectAll('.node').classed('pulse', false);
                                d3.selectAll('.link').classed('active', false);
                                statusIndicator.classList.remove('show');
                            }, 3000);
                        }, 1000);
                    }
                }, step.delay);
            });
        }

        function showStatus(message) {
            statusIndicator.textContent = message;
            statusIndicator.classList.add('show');
        }

        // 点击空白处隐藏工具提示
        document.addEventListener('click', function(event) {
            if (!event.target.closest('.node')) {
                tooltip.classList.remove('show');
            }
        });

        // 响应式调整
        function adjustForMobile() {
            if (window.innerWidth <= 768) {
                // 移动端调整节点位置
                const mobileNodes = nodes.map(node => ({
                    ...node,
                    x: node.x * 0.8,
                    y: node.y * 0.9
                }));
                
                // 更新节点位置
                nodeElements.transition()
                    .duration(500)
                    .attr('transform', d => `translate(${d.x * 0.8}, ${d.y * 0.9})`);
                
                // 更新连接线
                linkElements.transition()
                    .duration(500)
                    .attr('d', d => {
                        const source = nodes.find(n => n.id === d.source);
                        const target = nodes.find(n => n.id === d.target);
                        
                        const sx = source.x * 0.8;
                        const sy = source.y * 0.9;
                        const tx = target.x * 0.8;
                        const ty = target.y * 0.9;
                        
                        if (d.style === 'dashed') {
                            return `M ${sx + 48} ${sy + 27} Q ${(sx + tx) / 2} ${(sy + ty) / 2 - 27} ${tx} ${ty + 13.5}`;
                        }
                        
                        if (Math.abs(sy - ty) < 10) {
                            return `M ${sx + 48} ${sy + 13.5} L ${tx} ${ty + 13.5}`;
                        } else {
                            return `M ${sx + 24} ${sy + 27} L ${tx + 24} ${ty * 0.9}`;
                        }
                    });
            }
        }

        window.addEventListener('resize', adjustForMobile);
        adjustForMobile(); // 初始调用
    </script>
</body>
</html>
